package la.common.xml; import org.w3c.dom.Attr; 
import org.w3c.dom.DOMException; 
import org.w3c.dom.Document; 
import org.w3c.dom.Element; 
import org.w3c.dom.Node; 
import org.w3c.dom.NamedNodeMap; 
 /** 
 * Add required namespace declarations. 
 */ 
public class NamespaceCorrector { 
   private static final String XMLNS_NS 
      = "http://www.w3.org/2000/xmlns/"; 
   private NamespaceCorrector() { 
   } 
   /** 
    * @param node The top node of target nodes 
    */ 
   public static void correct(Node node) { 
      switch (node.getNodeType()) { 
      case Node.ELEMENT_NODE: 
         correctElement((Element)node); 
                             // Fall down 
      case Node.DOCUMENT_NODE: 
      case Node.DOCUMENT_FRAGMENT_NODE: 
      case Node.ENTITY_REFERENCE_NODE: 
         for (Node ch = node.getFirstChild(); 
             ch != null; 
             ch = ch.getNextSibling()) { 
            correct(ch); 
         } 
         break; 
      } 
   } 
   /** 
   * Check whether the prefixes and the namespaces of el and 
   * its attributes are declared or not. 
   * if not, add a namespace declaration to el. 
   */ 
   private static void correctElement(Element el) { 
      // Check el. 
      String prefix = el.getPrefix(); 
      String current = el.getNamespaceURI(); 
      String declared = howDeclared(el, prefix); 
      if (prefix == null) { 
         if (current == null && declared == null) { 
            // ok 
         } else if (current == null || declared == null) { 
            set(el, prefix, current == null ? "" : current); 
         } else if (!current.equals(declared)) { 
            set(el, prefix, current); 
         } 
      } else { 
         if (current == null) 
            throw new DOMException(DOMException.NAMESPACE_ERR, 
                                   el.getNodeName() 
                                   +" has no namespace"); 
         if (declared == null || !current.equals(declared)) 
            set(el, prefix, current); 
      } 
      // Check attributes of el. 
      NamedNodeMap map = el.getAttributes(); 
      for (int i = 0;  i < map.getLength();  i++) { 
         Attr attr = (Attr)map.item(i); 
         prefix = attr.getPrefix(); 
         if (prefix == null || prefix.equals("xml") 
            || prefix.equals("xmlns")) 
            continue; 
         current = attr.getNamespaceURI(); 
         declared = howDeclared(el, prefix); 
         if (declared == null || !current.equals(declared)) { 
            set(el, prefix, current); 
            i = -1;         // map has changed. 
                            // So restart the loop. 
         } 
      } 
   } 
   private static void set(Element el, String prefix, String ns) { 
      String qname = prefix == null ? "xmlns" : "xmlns:"+prefix; 
      if (el.getAttributeNode(qname) != null) 
         throw new DOMException(DOMException.NAMESPACE_ERR, 
                                "Namespace inconsistence"); 
      el.setAttributeNS(XMLNS_NS, qname, ns); 
   } 
   /** 
    * Search <var>context</var> and ancestors for declaration 
    * of prefix. 
    * @param prefix Prefix, or <code>null</code> for default ns. 
    */ 
   private static String howDeclared(Element context, 
                                     String prefix) { 
      String qname = prefix == null ? "xmlns" : "xmlns:"+prefix; 
      for (Node node = context; node != null; 
          node = node.getParentNode()) { 
         if (node.getNodeType() == Node.ELEMENT_NODE) { 
            Attr attr = ((Element)node).getAttributeNode(qname); 
            if (attr != null) { 
               if (prefix == null && 
                      attr.getNodeValue().equals("")) 
                   return null; 
               else 
                   return attr.getNodeValue(); 
            } 
         } 
      } 
      return null; 
   } 
} 